﻿using System;
using System.Collections.Generic;
using System.Web;
using log4net;

namespace MyLib.Web {
	/// <summary>
	/// 
	/// </summary>
	/// <typeparam name="THandler"></typeparam>
	public sealed class HttpHandlerResourceManager<THandler> where THandler : IHttpHandler {
		private ILog m_logger = log4net.LogManager.GetLogger(typeof(HttpHandlerResourceManager<THandler>));

		private const string SessionFactoryKey = "__session_factory";

		private const string CachedDbProvidersKey = "__cached_dbproviders";

		private THandler m_owner;

		private HttpContext m_httpContext;

		private IDictionary<string, object> m_resources;

		/// <summary>
		/// 
		/// </summary>
		/// <param name="owner"></param>
		/// <param name="context"></param>
		public HttpHandlerResourceManager(THandler owner, HttpContext context) {
			System.Diagnostics.Debug.Assert(owner != null);
			System.Diagnostics.Debug.Assert(context != null);

			m_owner = owner;
			m_httpContext = context;
			m_resources = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);

			Add(SessionFactoryKey, SessionFactory.Create());
			Add(CachedDbProvidersKey, new Dictionary<Type, DbContextProvider>());
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="key"></param>
		/// <param name="value"></param>
		public void Add(string key, object value) {
			System.Diagnostics.Debug.Assert(!string.IsNullOrWhiteSpace(key));
			System.Diagnostics.Debug.Assert(value != null);

			m_resources[key] = value;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <typeparam name="TRes"></typeparam>
		/// <param name="key"></param>
		/// <returns></returns>
		public TRes Get<TRes>(string key) {
			return (TRes)m_resources[key];
		}

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		public SessionFactory GetSessionFactory() {
			return Get<SessionFactory>(SessionFactoryKey);
		}

		private static Func<ICacheProvider> m_createCacheProviderDelegate1 = null;

		private static Func<System.Web.HttpContext, ICacheProvider> m_createCacheProviderDelegate2 = null;

		/// <summary>
		/// 
		/// </summary>
		/// <returns></returns>
		private ICacheProvider CreateCacheProvider() {
			if (m_createCacheProviderDelegate1 != null) {
				return m_createCacheProviderDelegate1();
			} else if (m_createCacheProviderDelegate2 != null) {
				return m_createCacheProviderDelegate2(m_httpContext);
			} else {
				var cpTypeName = System.Configuration.ConfigurationManager.AppSettings["MyPage.CacheProviderType"];
				if (cpTypeName == null || cpTypeName.Length == 0) {
					cpTypeName = typeof(WebCacheProvider).FullName;
				}

				var cpType = Type.GetType(cpTypeName);
				if (cpType == null) {
					m_logger.ErrorFormat("Cannot load cache provider \"{0}\", use default cache provider instead.", cpTypeName);
					cpType = typeof(WebCacheProvider);
				}

				var ctt = 1;
				var ct = cpType.GetConstructor(System.Type.EmptyTypes);
				if (ct == null) {
					ct = cpType.GetConstructor(new Type[] { typeof(System.Web.HttpContext) });
					ctt = 2;
				}
				if (ct == null) {
					m_logger.ErrorFormat("Canno find suitable constructor of cache provider \"{0}\". use default cache provider instead.", cpTypeName);
					ct = typeof(WebCacheProvider).GetConstructor(new Type[] { typeof(System.Web.HttpContext) });
					ctt = 2;
				}

				if (ctt == 1) {
					m_createCacheProviderDelegate1 = System.Linq.Expressions.Expression.Lambda<Func<ICacheProvider>>(
						System.Linq.Expressions.Expression.New(ct)).Compile();
					return m_createCacheProviderDelegate1();
				} else if (ctt == 2) {
					var parameterContext = System.Linq.Expressions.Expression.Parameter(typeof(System.Web.HttpContext), "context");
					m_createCacheProviderDelegate2 = System.Linq.Expressions.Expression.Lambda<Func<System.Web.HttpContext, ICacheProvider>>(
						System.Linq.Expressions.Expression.New(ct, parameterContext),
						parameterContext).Compile();
					return m_createCacheProviderDelegate2(m_httpContext);
				} else {
					System.Diagnostics.Debug.Assert(false, "ctt = " + ctt + "?!");
					return null;
				}
			}
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="pType"></param>
		/// <returns></returns>
		public DbContextProvider GetDbProvider(Type pType) {
			DbContextProvider t = null;

			var cachedProviders = Get<IDictionary<Type, DbContextProvider>>(CachedDbProvidersKey);

			if (cachedProviders.ContainsKey(pType)) {
				t = cachedProviders[pType];
			} else {
				t = Activator.CreateInstance(pType) as DbContextProvider;

				if (t != null) {
					t.SessionFactory = Get<SessionFactory>(SessionFactoryKey);

					cachedProviders.Add(pType, t);

					if (t is ICacheable) {
						((ICacheable)t).CacheProvider = CreateCacheProvider();
					}
				}
			}

			return t;
		}

		/// <summary>
		/// Close manager and dispose all resources.
		/// </summary>
		public void Close() {
			foreach (var et in m_resources) {
				if (et.Value is System.IO.TextReader) {
					var reader = (System.IO.TextReader)et.Value;
					try {
						reader.Close();
					} catch {
					}
				} else if (et.Value is System.IO.TextWriter) {
					var writer = (System.IO.TextWriter)et.Value;
					try {
						writer.Flush();
					} catch {
						try {
							writer.Close();
						} catch {
						}
					}
				} else if (et.Value is System.IO.Stream) {
					var stream = (System.IO.Stream)et.Value;
					try {
						stream.Flush();
					} catch {
						try {
							stream.Close();
						} catch {
						}
					}
				} else if (et.Value is IDisposable) {
					try {
						((IDisposable)et.Value).Dispose();
					} catch {
					}
				}
			}
		} // end of Close.
	} // end of HttpHandlerResourceManager.
}
